(********************************************************************)
(*	file:		XCmdGlue.Inc										*)
(*	version:	.5alpha												*)
(*																	*)
(* Koscheka's support routines for  Appletalk/Hypercard interface.	*)
(* ----------------------------------------------------------------	*)
(* By:		Donald Koscheka											*)
(* Date:	2-Feb-88												*)
(* © Copyright 1987, Apple Computer, Inc.							*)
(* All Rights Reserved												*)
(*																	*)
(* ----------------------------------------------------------------	*)
(*						Modification History						*)
(* ----------------------------------------------------------------	*)
(*  Date	   	| By|	 				Description					*)
(* ----------------------------------------------------------------	*)
(*  2-Feb-88	| DK| File Created									*)
(*  2-Mar-88	| DK| Added connect routine for adsp				*)
(* 17-Mar-88	| DK| ALPHA RELEASE									*)
(* 26-Apr-88	| DK| Added Zone Lookup Routine (LookUpZones)		*)
(* 12-May-88	| DK| DSPHangup Unconditionally regardless of 		*)
(*				|	| connection state								*)
(* 13-May-88	| DK| DSPAttention message to the library			*)
(* ----------------------------------------------------------------	*)
(********************************************************************)


(********************************************************************)
(*  Assumes the XCMD has included this file and has named its 		*)
(*	argument "paramPtr".											*)
(********************************************************************)


Procedure SaveHandle( theID: Integer; theHandle: LongInt );
{**********************************
* Save a handle away in the Hypercard
* global heap.  The name of the global 
* variable to use is stored in the 
* resource file.  Extract that name,
* get a handle to the data and do the
* dirty deed.
*
* In:	theID 		= resource id of the container
*		theHandle 	= thehandle or pointer to save away
*
**********************************}
VAR
	name		: StringHandle;
	DataHandle 	: Handle;
	buffHandle	: Str31;

BEGIN
	name 		:= StringHandle(GetResource('STR ', theID ));
	
	IF theHandle <> 0 THEN
		BEGIN
			buffHandle	:= LongToStr( theHandle );
			DataHandle	:= PasToZero( buffHandle );
		END
	ELSE
		BEGIN
			DataHandle 		:= NewHandle( 1 );
			DataHandle^^ 	:= 0;
		END;
		
	{*** DataHandle will either be the string representation of the handle	***}
	{*** or it will be NIL which Hypercard will take as EMPTY				***}
	
	IF name <> NIL THEN 
		BEGIN
			HLock( Handle(name) );
			SetGlobal( name^^ , DataHandle );
			
			IF DataHandle <> NIL THEN
				DisposHandle( DataHandle );
			HUnlock( Handle(name) );
		END;
END;


Function RetrieveHandle( theID: Integer): LongInt;
{**********************************
* Retrieve a handle away from the Hypercard
* global heap.  The name of the global 
* variable to use is stored in the 
* resource file.  Extract that name,
* get a handle to the data and 
* return the data in that container.
* note that hypercard stores all of
* its data as null-strings, so we must
* convert it back to a handle...
*
* In:	theID = id of the handle or pointer
*				to retrieve.
*
**********************************}
VAR
	DataHandle		: Handle;
	name 			: StringHandle;
	localString		: Str255;
	returnHandle	: LongInt;
	
BEGIN	
	name := StringHandle( GetResource('STR ', theID ) ); 	
	DataHandle := NIL;
	
	IF name <> NIL THEN
		BEGIN
			HLock( Handle(name) );						
			DataHandle := GetGlobal( name^^ ); 
			HUnlock( Handle(name) );	
		END;
		
	IF DataHandle <> NIL
		THEN
			BEGIN
				HLock( DataHandle );
				ZeroToPas( Ptr(DataHandle^), localString);
				returnHandle := StrToLong( localString );
				HUnlock( DataHandle );
				DisposHandle( DataHandle );
				RetrieveHandle := LongInt(returnHandle);
			END
		ELSE
			RetrieveHandle := 0;
END;


Function ErrorReturn( theErr: OSErr ): Handle;
{**********************************
* Given a system error number in theErr
* extract its string from the resource
* fork and returns it to HyperCard
*
* If we can't work out the error string,
* build one using the error number.
**********************************}
VAR
	strhand	: Handle;
	s,num	: Str255;
	len		: LongInt;

BEGIN	
	ErrorReturn := NIL;
	s := '';
	
	IF theErr <> noErr THEN
		BEGIN
	
			strhand := GetResource( 'STR ', theErr );
			IF strhand <> NIL THEN
				BEGIN
					len := GetHandleSize( strhand );
					BlockMove( Ptr(strhand^), @s, len );
					ErrorReturn := PasToZero( s );
				END
			ElSE
				BEGIN
					s := 'ERROR ';
					num := NumToStr( LongInt(theErr) );
					
					s := Concat( s, num );
					len := LongInt( Length( s ) ) + 1;
					
					IF len <> 0 THEN
						ErrorReturn := PasToZero( s );
				END;
		END;
END;

Function StrCmp( s1, s2 : String ): Boolean;
{**********************************
* Given two strings, return true if they
* are exact matches, false otherwise
**********************************}
VAR 
	i,j			: Integer;
	matching	: Boolean;
BEGIN
	matching := true;
	i := Integer( s1[0] );	{*** get the number of characters in the string ***}
	j := Integer( s2[0] );	{*** as well as the comparison string			***}
	IF i = j THEN			{*** if not the same length, not the same string***}
		WHILE i > 0 DO
				BEGIN
					WHILE matching DO
						IF s1[i] <> s2[i] THEN {*** strings don't match		***}
							BEGIN
								matching := false;
								i := 0;
							END;
					i := i - 1;
				END;
	StrCmp := matching;
END;


PROCEDURE DSPKillData( cb : cbPtr );
(****************************************)
(* Either we could not allocate enough	*)
(* memory or the connection would not 	*)
(* initialize, dump all allocated data	*)
(* 										*)
(****************************************)
BEGIN
	IF cb <> NIL THEN
	WITH cb^ DO
		BEGIN
		{*** could not allocate or initialize ***}
			IF sendQ <> NIL THEN DisposPtr( sendQ );
			IF recvQ <> NIL THEN DisposPtr( recvQ );
			IF attn  <> NIL THEN DisposPtr( attn );
			
			IF remName <> NIL THEN 
				BEGIN
					HUnlock( remName );
					DisposHandle( remName );
				END;
				
			IF msg <> NIL THEN 
				BEGIN
					HUnlock( msg );
					DisposHandle( msg );
				END;

			IF inBuf <> NIL THEN 
				BEGIN
					HUnlock( inBuf );
					DisposHandle( inBuf );
				END;
				
			IF outBuf <> NIL THEN 
				BEGIN
					HUnlock( outBuf );
					DisposHandle( outBuf );
				END;
				
			IF attnBuf <> NIL THEN 
				BEGIN
					HUnlock( attnBuf );
					DisposHandle( attnBuf );
				END;
				
			DisposPtr( Ptr( cb ));
		END;
END;



FUNCTION NBPGetAddress(	name : Ptr ): AddrBlock;
(************************************************)
(* Given the name of an entity, walk through the*)
(* names table searching for the name and return*)
(* the address if found, NIL otherwise			*)
(*												*)
(* Note requires access to the ADSP pointer!!!	*)
(*												*)
(* In:	Pointer to the name of the entity		*)
(*												*)
(************************************************)
VAR
	error		: Integer;
	nbp			: NBPPtr;
	found		: Boolean;
	eName		: EntityName;
	eAddr		: AddrBlock;	
	who			: Integer;
	str			: StringPtr;
	pName		: Str255;

BEGIN
	found 		:= FALSE;
	who 		:= 1;
	nbp 		:= NBPPtr( RetrieveHandle(  GLOBALNBPDATA ) );
	str 		:= StringPtr( @eName.objStr );
	ZeroToPas( name, pName );
	NBPGetAddress.aNet := 0;
	NBPGetAddress.aNode := 0;
	NBPGetAddress.aSocket := 0;
	
	IF ( nbp <> NIL ) AND (nbp^.LookUpBuffer <> NIL) THEN 
		BEGIN
			HLock( Handle(nbp^.LookUpBuffer) );
			IF (nbp^.EntCount > 0) THEN
				REPEAT
					error := NBPExtract( Ptr(nbp^.LookUpBuffer^),nbp^.EntCount,who,eName,eAddr);
		 			 IF error = noErr THEN
						BEGIN
							found := StringEqual(  str^, pName );
							IF found THEN
								NBPGetAddress := eAddr
							ELSE
								who := who + 1;
						 	IF (who > nbp^.EntCount) THEN found := TRUE;
						END
					  ELSE
					  	found := TRUE;
				UNTIL found;
			HUnlock( Handle(nbp^.LookUpBuffer) );
		END;
END;


FUNCTION NBPGetName( addr : AddrBlock ) : Handle;
(************************************************)
(* Given the address of an entity, walk through *)
(* the names table searching for the address and*)
(* return the name of the entity, nil if not 	*)
(*												*)
(* Note requires access to the ADSP pointer!!!	*)
(*												*)
(* In:	Address of the entity					*)
(* Out:	Handle containing the name of the entity*)
(* 		as a zero-terminated string				*)
(*												*)
(************************************************)
VAR
	error		: Integer;
	nbp			: NBPPtr;
	done		: Boolean;
	eName		: EntityName;
	eAddr		: AddrBlock;	
	who			: Integer;
	yourName	: Handle;

BEGIN
	done 		:= FALSE;
	who 		:= 1;
	nbp 		:= NBPPtr( RetrieveHandle(  GLOBALNBPDATA ) );
	yourName	:= NIL;
	
	IF (nbp <> NIL) AND (nbp^.LookUpBuffer <> NIL) THEN 
		BEGIN
			HLock( nbp^.LookUpBuffer );
			IF (nbp^.EntCount > 0) THEN
				REPEAT
					error := NBPExtract( nbp^.LookUpBuffer^,nbp^.EntCount,who,eName,eAddr);
		 			IF error = noErr THEN
						BEGIN
							IF LongInt(addr) = LongInt(eAddr) THEN
								yourName := PasToZero( eName.objStr )
							ELSE
								who := who + 1;
								
						 	IF (who > nbp^.EntCount) THEN
								done := TRUE;
						END
					  ELSE
					  	done := TRUE;
				UNTIL (done) OR (yourName <> NIL );
				
			HUnlock( nbp^.LookUpBuffer );
		END;
	NBPGetName := yourName;
END;


PROCEDURE DSPAddConnection( cb : CBPtr );
(********************************************)
(* If we have a good connection, we can		*)
(* go ahead and put it into the	connection 	*)
(* list.  It is either the first item in 	*)
(* the list or we append it to the end by 	*)
(* traversing the list to find the last item*)
(********************************************)
VAR
	nb, lb	: CBPtr;
BEGIN
	nb 	:= adsp^.ends;

	IF cb <> NIL THEN
		IF nb = NIL THEN
			adsp^.ends := cb
		ELSE
			BEGIN	
				WHILE nb <> NIL DO {*** walk to the end of the list		***}
					BEGIN
						lb := nb;
						nb := nb^.next;
					END;
					
				lb^.next := cb;
				cb^.next := NIL;
				cb^.last := lb;
			END;
END;


PROCEDURE DSPDisConnect( cn : CBPtr );
(************************************************)
(* Tear down an ADSP connection, deallocate the *)
(* memory and remove the entity from the 		*)
(* connection list.								*)
(*												*)
(* Note requires access to the ADSP pointer!!!	*)
(*												*)
(* IN:	pointer to the connection's block		*)
(*												*)
(************************************************)
VAR
	nb, lb	: CBPtr;
	dpb		: dspParamBlock;
	tH		: Handle;
	
BEGIN
	IF cn <> NIL THEN 
		BEGIN
			IF cn^.remName <> NIL THEN
				BEGIN
					SetGlobal( 'ADSPCaller', cn^.remName ); 
					SendCardMessage( 'HungUp');
				END;
				
			{***  remove connection from connection list	***}
			IF cn = adsp^.ends THEN {*** special case: cn = first ***}
				BEGIN
					adsp^.ends := cn^.next;
					nb := cn^.next;
					IF nb <> NIL THEN nb^.last := NIL;
				END
			ELSE
				BEGIN
					nb := cn^.next;
					lb := cn^.last;
					IF nb <> NIL THEN nb^.last := lb;
					IF lb <> NIL THEN lb^.next := nb;
				END;
		
			DSPKillData( cn );
		END;
END;	





Function ADSPAttention( cn; cbPtr: Ptr; len : Integer  ): OSErr;
(************************************************)
(* Send the message of length len to the 		*)
(* internet address specified by eAddr.			*)
(************************************************)
VAR
	error		: OSErr;
	HSiz		: LongInt;
	
{$I XCmdGlue.inc }
{$I XCMDADSP.inc }	


BEGIN
	error := errAttention;

	{*** check that the connection is not busy first ***}
	IF {  connection is busy } THEN
	ELSE
		BEGIN
			cn^.attnBuf := NIL; {*** assume we won't allocate the handle ***}
							
			{*** (1) Copy the data into a local buffer	***}
				IF msg <> NIL THEN
					BEGIN
						IF len > attnBufSize THEN
							len := attnBufSize;
						
						cn^.attnBuf := NewHandle( LongInt(len) );
					END;
				
				IF cn^.attnBuf <> NIL THEN
					BEGIN
						MoveHHi( cn^.attnBuf );
						HLock( cn^.attnBuf );
						BlockMove( msg, cn^.attnBuf^, LongInt(len) );
						
						{*** (2) Send the data to the other end 	***}
						WITH cn^.attnPB DO
							BEGIN
								ioCompletion:= NIL;
								ioCRefNum 	:= adsp^.dspRefNum;
								csCode 		:= dspAttention;
								ccbRefNum	:= cn^.ccbRef;
								attnSize	:= len;
								attnData	:= cn^.attnBuf^;
								attnInterval:= 0;
							END;
						error := PBControl( @cn^.attnPB, ASYNC );
					END;
		END;
	DSPAttention := error;
END;




FUNCTION DSPCreate(	addr:AddrBlock; 
					inBufSiz,outBufSiz,retry,interval:Integer;
					message: Handle ): CBPtr;
(************************************************)
(* Creates an ADSP connection, adds it to the 	*)
(* connection list, initializes the  connection *)
(* Attempts to discover the name of the enitity	*)
(* Because this routine sets up all outgoing and*)
(* incoming calls should have a name associated	*)
(* with them by this routine...					*)
(*												*)
(* If the connection is already made, return a	*)
(* the pointer to the connection.				*)
(*												*)
(* Note requires access to the ADSP pointer!!!	*)
(*												*)
(* IN:	addr			: address of the entity	*)
(*		inBufSiz, outBufSiz						*)
(*						: size of the buffers	*)
(*		retry			: number of times to 	*)
(*						  retry the open		*)
(*		interval		: 10Xticks time to wait	*)
(*						  between retries		*)
(* 		mess			: callback message		*)
(*												*)
(* OUT:											*)
(* 		CBPtr			: return a pointer to 	*)
(*						  the connection, NIL if*)
(*						  connection not inited	*)
(*												*)
(************************************************)
VAR
	error 		: Integer;
	siz			: LongInt;
	nb, cb		: CBPtr;
	nbp			: NBPPtr;
	
BEGIN
	{*** see if the connection is already established ***}
	nb := adsp^.ends;		
		
	cb := CBPtr(  NewPtr( SizeOf( Connection ) ));
	IF cb <> NIL THEN 
		WITH cb^ DO				{*** Allocate connection's structures	***}
			BEGIN
				ccbRef	:= 0;
				msg		:= NIL;
				last 	:= NIL;
				next 	:= NIL;
				adr		:= addr;	{*** address of the remote end 	***}
				inBuf	:= NIL;		{*** no data received yet		***}
				outBuf	:= NIL;		{*** no data sent yet			***}
				attnBuf	:= NIL;		{*** no attention message...	***}
				mode	:= NOP;		{*** connection not established	***}
				sendQ 	:= NewPtr( LongInt(outBufSiz) );
				recvQ 	:= NewPtr( LongInt(inBufSiz) );
				attn	:= NewPtr( attnBufSize );
				
				{*** (3) Initialize the connection end 						***}
				IF (sendQ<>NIL) AND (recvQ<>NIL) AND (attn<>NIL) THEN
				BEGIN
					IF message <> NIL THEN
						BEGIN
							siz	:= GetHandleSize( message );
							msg	:= NewHandle( siz );
							BlockMove( message^ , msg^, siz );
						END;
										
					WITH attnPB DO 
						BEGIN
							userRoutine	:= NIL;
							ioResult	:= 1;	{*** mark it as busy***}
						END;
						
					ccb.userFlags := 0;
					
					WITH dspPB DO {*** Initialize the connection end ***}
						BEGIN
							csCode		:= dspInit;
							ccbPtr		:= @cb^.ccb;
							userRoutine	:= NIL;
							ioCRefnum	:= adsp^.dspRefNum;
							ioCompletion:= NIL;
							sendQSize	:= outBufSiz;
							sendQueue	:= sendQ;
							recvQueue	:= recvQ;
							recvQSize	:= inBufSiz;
							attnPtr		:= attn;
							localSocket	:= adsp^.addr.aSocket;
						END;
					error := PBControl( @cb^.dspPB, SYNC );
					
					IF error = noErr THEN {*** Add connection to list if init ok ***}
						BEGIN
							ccbRef	:= dspPB.ccbRefNum;
							DSPAddConnection( cb );								
						END
					ELSE	
						DSPKillData( cb );
					
				END { IF data allocated OK }
				ELSE{ data did not allocate or connection did not initialize }
					DSPKillData( cb );
			END;		
	DSPCreate := cb;
END;	



FUNCTION DSPHangUp( cn : CBPtr ):OSErr;
(************************************************)
(* Hangup the line on requested connection end	*)
(*												*)
(* Note requires access to the ADSP pointer!!!	*)
(*												*)
(* IN:	pointer to the connection's block		*)
(*		handle to the data to send				*)
(*												*)
(* 5/12/88 Connection is unconditionally removed*)
(* We do not have to know the state of the 		*)
(* connection to remove it.						*)
(************************************************)
VAR
	myPB	: DSPParamBlock;

BEGIN
	IF cn <> NIL  THEN		{*** If the connection exists, tear it down	***}
		BEGIN
			WITH myPB DO
				BEGIN
					ioCRefNum	:= adsp^.dspRefNum;
					ccbRefNum	:= cn^.ccbRef;
					csCode		:= dspRemove;
					abort		:= -1;
					
					{*** Hangup synchronously because the state of the
						connection is about to go ballistic	***}
				END;
				DSPHangup := PBControl( @myPB, SYNC );
			DSPDisConnect( cn );
		END;
END;	



FUNCTION DSPTalk(	cn : CBPtr; bufH : Handle ):OSErr;
(************************************************)
(* Send the data to the requested connection end*)
(*												*)
(* Note requires access to the ADSP pointer!!!	*)
(*												*)
(* IN:	pointer to the connection's block		*)
(*		handle to the data to send				*)
(*												*)
(************************************************)
VAR	
	Hsiz	: LongInt;
	
BEGIN
	{*** (1) Copy the data into a local buffer	***}
	cn^.outBuf := NIL; {*** assume we won't allocate the handle ***}
	
	IF bufH <> NIL THEN
		BEGIN
			HSiz := GetHandleSize( bufH );
			cn^.outBuf := NewHandle( HSiz );
		END;
	
	IF cn^.outBuf <> NIL THEN
		BEGIN
			MoveHHi( cn^.outBuf );
			HLock( cn^.outBuf );
			BlockMove( bufH^, cn^.outBuf^, HSiz );
			
			{*** (2) Send the data to the other end 	***}
			WITH cn^.dspPB DO
				BEGIN
					ioCompletion:= NIL;
					ioCRefNum 	:= adsp^.dspRefNum;
					csCode 		:= dspWrite;
					ccbRefNum	:= cn^.ccbRef;
					reqCount	:= Integer( HSiz );
					dataPtr		:= cn^.outBuf^;
					eom			:= 1;
					flush		:= 1;
				END;
			DSPTalk := PBControl( @cn^.dspPB, ASYNC );
		END;
END;


PROCEDURE p2cStr( myPtr : Ptr );
VAR
	i 	: LongInt;
	aPtr: Ptr;
BEGIN
	FOR i := 0 to myPtr^ DO
		BEGIN
			aPtr := Pointer( ORD( myPtr ) + 1 );
			myPtr^ := aPtr^;
			myPtr := aPtr;
		END;
	myPtr := Pointer( ORD( myPtr ) - 1 );
	myPtr^ := 0;
END;

FUNCTION LookUpZones( AllZones : BOOLEAN ): Handle;
(***********************************
* Get information about the current zone 
* or about all zones (whichmode is set accordingly
*
* Written by and with thanks to Randy Carr
* Modified by D. Koscheka
*
* IN: 	AllZones set TRUE if lookUp all zones,
*		false if lookup current zone only
*
* OUT:	zone information table as a null terminated string
***********************************)
VAR
	OsError				: OsErr;
	index, templen,
	dummy, j,i,foundit,
	datalen				: INTEGER;
	tempbyte			: SignedByte;
	WereDone			: BOOLEAN;
	tempstr				: Str255;
	temprect			: Rect;
	GlobBrNode			: SignedByte;
	GlobBrNet,
	NumZones,StartIndex	: INTEGER;
	aTalkGlobals		: LIntPtr;
	zonesPtr			: PACKED ARRAY[0..577] OF SignedByte;
	theBDS				: BDSType;
	MPPpb				: ATPParamBlock;
	ZoneTable, tempName	: Handle;
	tempPtr				: Ptr;
	oldSize, newSize 	: LongInt;
	
	Procedure GetPZones( AllZones : BOOLEAN );
		var	i : INTEGER;
		BEGIN 	{*** GetPZones ***}
			WITH MPPpb DO 
				BEGIN
					ioCompletion := NIL;
					IF AllZones THEN
						userData := $08000000 + StartIndex {*** get zone list 	***}
					ELSE
						userData := $07000000; {*** get name of this zone 		***}
					
					atpFlags 			:= 0;
					addrBlock.aNet 		:= GlobBrNet;
					addrBlock.aNode 	:= GlobBrNode;
					addrBlock.aSocket 	:= 6; {*** socket 6 = ZIP socket 		***}
					reqLength			:= 0; {*** make a clean ATPRequest		***}
					reqPointer			:= NIL; 
					bdsPointer 			:= @theBDS;
					numOfBuffs			:= 1;
					timeOutVal			:= 1;
					retryCount			:= 5;
				END;
				
				theBDS[0].buffSize := 578;
				theBDS[0].buffPtr := @zonesPtr;
				osError := PSendRequest( @MPPpb, SYNC );
				
		END;	{*** GetPZones ***}
		
	{***------------------------------------------------------------------------***}	
	BEGIN	{*** LookUpZones ***}
		aTalkGlobals:= LIntPtr( aTalkVars ); {*** point to the lowMem global	***}
		GlobBrNet	:= IntPtr( ORD(aTalkGlobals^)+SysNetNum )^;
		GlobBrNode 	:= Ptr( ORD(aTalkGlobals^)+SysABridge)^;
		GlobBrNode	:= BAND( $00FF, GlobBrNode );		{*** Byte it off ***}
		
		IF GlobBrNode = 0 THEN EXIT( LookUpZones ); {*** Bridge timed-out		***}
		StartIndex 	:= 1;
		WereDone 	:= TRUE;
		ZoneTable := NewHandle( 1 );
		ZoneTable^^ := 0;
		
		IF AllZones THEN
		BEGIN
			REPEAT
				GetPZones( AllZones );
				IF OsError <> NoErr THEN
					BEGIN
						paramPtr^.returnValue := ErrorReturn( OsError );
						EXIT( LookUpZones );
					END
				ELSE
					BEGIN
						WereDone := (BAND($FF000000,theBDS[0].userBytes)<>0);
						NumZones := (BAND($0000FFFF,theBDS[0].userBytes));
					END;
					
				StartIndex := StartIndex + NumZones;
				index := 0;	{*** index into the zone list ***}
				
				IF NumZones > 0 THEN
					FOR i := 1 TO NumZones DO
					BEGIN
						{*** Extract the name from the response BDS Packet 	***}
						TempPtr := Pointer( ORD(@zonesPtr[index]) );
						TempByte := zonesPtr[index];
						TempLen := ORD( TempByte );
						p2cStr( TempPtr );
						
						IF i < NumZones THEN 
						BEGIN
							TempPtr := Pointer( ORD(TempPtr) + TempLen );
							TempPtr^ := $0D;
						END;
						index := index + tempLen+1;
					END; {*** FOR i := 1 TO numZones ***}
					
				TempLen := GetHandleSize( ZoneTable );
				SetHandleSize( ZoneTable, TempLen+LongInt( index ) );
				BlockMove(Pointer(ORD(@zonesPtr)),Pointer(ORD(zoneTable^)+TempLen-1),index );
			UNTIL WereDone;
			
			TempLen := GetHandleSize( ZoneTable );
			SetHandleSize( ZoneTable, TempLen + 1 );
			TempPtr := Pointer( ORD(ZoneTable^) + TempLen + 1);
			TempPtr^ := 0;
		END 
		ELSE
			BEGIN 	{*** ELSE whichMode = getOurZone ***}
				GetPZones( FALSE );
				IF OsError <> noErr THEN
					BEGIN
						paramPtr^.returnValue := ErrorReturn( OsError );
						EXIT( LookUpZones );
					END;
				tempByte 	:= ZonesPtr[0];
				tempLen 	:= ord(tempByte);
				FOR i:=0 TO tempLen DO
					tempstr[i] := chr(zonesPtr[i]);
				ZoneTable 	:= PasToZero( tempstr );
				
			END; 	{*** ELSE whichMode = getOurZone ***}
		
		LookUpZones := ZoneTable;
	END;	{*** LookUpZones ***}
	
	
	
	
	
	